#ifndef CAMERA_HPP
#define CAMERA_HPP

// class definitions for all global classes
// list of global classes:
// camera, ground
#include "stdlib.h"
#include <iostream>
#include "math.h"
#include "GlobalDefines.hpp"
#include "geometry/Axes.hpp"
#include "State/App.hpp"

#define CAMERA_MODE_FREEFLY 10
#define CAMERA_MODE_ORBIT 20

#define CAMERA_MOVE_STOP 0
#define CAMERA_MOVE_FORWARD 1
#define CAMERA_MOVE_BACKWARD 2
#define CAMERA_MOVE_LEFT 3
#define CAMERA_MOVE_RIGHT 4
#define CAMERA_MOVE_UP 5
#define CAMERA_MOVE_DOWN 6


class Camera
{
public:
    Camera ();
    Camera (Vec3d nPosition, Axes nOrientation);
    void Place();

    void SetFoV (double newFoV);
    void SetRotationSensitivity (double newSens);
    void SetMovementSpeed (double newMoveSpeed);

    double GetFoV ();
    double GetAspect();
    double GetNearPlane(); 
    double GetFarPlane();
    void UpdateAspect(int w, int h);

    double GetMovementSpeed();
    double GetOrbitDistance();

    Vec3d GetLookAtDirection();        // returns a normalized vector pointing along the direction the camera is looking
    Vec3d GetPosition();        // returns camera position
    Vec3d GetForwardDirection();
    Vec3d GetUpDirection();
    Axes  GetOrientation();

    void  SetOrientation(Axes newOrientation);

    bool RotateHorizontal(double angle);
    bool RotateVertical(double angle);

    /* // commented out for now, but will need to re-instate these at some point
    void setModeTo(int CAMERA_MODE_);

    void setOrbitTarget (Vec3d *newTarget);
    void setOrbitDistance (double dist);
    void adjustOrbitdisanceBy( double d_dist);
    */

    void SetMovingForward(bool tf);
    void SetMovingBackward(bool tf);
    void SetMovingLeft (bool tf);
    void SetMovingRight (bool tf);
    void SetMovingUp (bool tf);
    void SetMovingDown (bool tf);

    void MoveTo (Vec3d newPosition, Axes newOrientation);

    void Update (double dTime);

    static Camera* GetInstance();

private:
    Vec3d position;

    /* Orientation: this is a set of 3 Vec3fs comprising an orthogonal axes.
    *  The assumptions here are the following:
    *  Camera will have a vertical rotation that will be based around these
    *  The horizontal rotation will rotate the whole axes
    *  Where the Axes.Y-axis will be UP
    *  And Axes.X-axis will be a forward direction
    */
    Axes orientation;

    /* lookAtDirection
    *  loodAtDirection is a normalized vector that stores the look-at-direction
    *  relative to the orientation axes. 
    *  This means that if we set this vector to (1,0,0)
    *  It will point along whatever the orientation's X-Axis is
    *  It is ALWAYS on the XY plane, since horizontal rotation affects the whole axis
    *  This vector is only useful for 'walking' on some area that has a up an down
    */
    Vec3d lookAtDirection;
    
    // this is temporarily meaningless
    int mode;           // mode - see above CAMERA_MODE_ defines

    bool isMovingForward;
    bool isMovingBackward;
    bool isMovingLeft;
    bool isMovingRight;
    bool isMovingUp;
    bool isMovingDown;
    bool rotated;

    double movementSpeed;    // speed of movement - units/second
    double rotationSensitivity;  // higher = more sensitive, lower = less

    Vec3d *orbitTarget;
    double orbitDistance;
    double orbitDistanceMin;
    double orbitDistanceMax;
    double orbitHorAngle;
    double orbitVerAngle;
    double orbitAdjustment;

    double FoV;            // field of view, required to be set for RotateTowards (x, y)
    double aspect;
    double nearPlane;
    double farPlane;

    static Camera* instancePointer;

    void CommonInit();
};

#endif

